<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
    <head>
	    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	    <title>liu zi qi</title>
	    <link rel="stylesheet" href=<?php echo base_url()."demo.css"; ?> type="text/css" media="screen">
	    <link rel="stylesheet" href=<?php echo base_url()."demo-print.css" ?> type="text/css" media="print">
	    <script src=<?php echo base_url()."raphael.js" ?> type="text/javascript" charset="utf-8"></script>
	    <script src=<?php echo base_url()."jquery.js" ?> type="text/javascript" charset="utf-8"></script>
	    <script type="text/javascript" charset="utf-8">

			"use strict"; 
			
			// Global variables
			var XORG = 50, YORG = 50, XDIS = 100, YDIS = 100, DISDELTA = 50, CWIDTH = 20;
			var COLS = 4, ROWS = 5, TOTALNUM = ROWS * COLS;
			var DELAY_NUM = 6;
			var DELAY_INTERVAL = 250;
			var STEP_INTERVAL = 500;
			var LEFT = 0, RIGHT = 1, UP = 2, DOWN = 3;
			var MAX_STEP = 5;
			var METHOD = 0;
	    var r;
	    // element: role
	    //          oldcx, oldcy(being set to cx/cy every move start)
	    //          counter(being set to zero every timeout)
	    //          failed(being set to 0 every move start)
	    //          oldfill(being initialized every move start)
	    var myset = new Array;
	    var mymatrix = new Array;
			var best_moves = new Array; // element: idx, way, role, score
			var lostidx1, lostidx2; // set to -1 when call judge() everytime
			var curmatrix = new Array;
			var hismatrix = new Array;
			var disable_state = 0;
			var white_turn = 0;
	
			function initial_curmatrix()
			{
				curmatrix = [
					1, 1, 1, 1,
					1, 0, 0, 1,
					0, 0, 0, 0,
					2, 0, 0, 2,
					2, 2, 2, 2,
					];
			}
			
			function initial_mymatrix(set, matrix)
			{
				var i, widx = 0, ridx = 0;
				var e;
	
				while (set.length > 0)
				{
					set.shift().hide();
				}

				for (i = 0; i < TOTALNUM; i++)
				{
					e = null;
					
					switch (curmatrix[i])
					{
					case 1:
						eval("e = r.circle(XORG + XDIS * " + i % COLS + ", YORG + YDIS * " + (i - i % COLS) / COLS + ", CWIDTH).attr({fill: \"#ffffff\", stroke: \"#000\"})");
						e.role = 1;
						break;
					case 2:
						eval("e = r.circle(XORG + XDIS * " + i % COLS + ", YORG + YDIS * " + (i - i % COLS) / COLS + ", CWIDTH).attr({fill: \"#000000\", stroke: \"#000\"})");
						e.role = 2;
						break;
					default:
						break;
					}
	
					if (e)
					{
						set.push(e);
		
						// 0: left, 1: right, 2: up, 3: down
						e.go = function (whichway){
							var dxs = [ -XDIS, XDIS, 0, 0 ];
							var dys = [ 0, 0, -YDIS, YDIS ];
							
							this.hide();
							this.attr(
								{cx:this.attr().cx += dxs[whichway], cy:this.attr().cy += dys[whichway]}
								);
							this.show();
						}
					}
					
					matrix[i] = e;
				}
			}
	
			function remove_ele(set, e)
			{
				var i;
	
				for (i = 0; i < set.length; i++)
				{
					if (set[i] == e)
					{
						break;
					}
				}
	
				if (i >= set.length)
				{
					alert("bug, not found\n");
				}
				else
				{
					var j;
	
					for (j = i; j < set.length - 1; j++)
					{
						set[j] = set[j + 1];
					}
	
					set.pop();
				}
			}
	
			function myidx(x, y)
			{
				var idx;
	
				idx = ((y - YORG) / YDIS) * COLS + (x - XORG) / XDIS;
	
				return idx;
			}
	
			function dragmove(dx, dy, x, y)
			{
				if (this.failed)
				{
					return;
				}
	
				this.hide();
				this.attr({cx: dx+this.oldcx, cy: dy+this.oldcy});
				this.show();
			}
	
			function dragstart(x, y)
			{
				this.failed = 0;
	
				if (this.role != 2)
				{
					this.failed = 1;
					return;
				}
	
				this.oldfill = this.attr().fill;
				this.attr({fill: "#0000ff", stroke: "#000"});
				this.oldcx = this.attr().cx;
				this.oldcy = this.attr().cy;
			}
	
			function judgeme(matrix, idx, role)
			{
				var ret = 0;
				var i, num, n, pow;
				var xidx, yidx, enemyidx;
	
				xidx = idx % COLS;
				yidx = (idx - xidx) / COLS;
	
				num = 0;
				n = 0;
				pow = 1;
				enemyidx = -1;
	
				for (i = 0; i < COLS; i++)
				{
					if (matrix[yidx * COLS + i])
					{
						n += pow;
	
						if (matrix[yidx * COLS + i] != role)
						{
							// at edge, not in middle
							if ((num == 0) || (num == 2))
							{
								enemyidx = yidx * 4 + i;
							}
						}
						else
						{
							num++;
						}
					}
	
					pow *= 10;
				}
	
				if ((n == 111) || (n == 1110))
				{
					if ((num == 2) && (enemyidx >= 0))
					{
						matrix[enemyidx] = 0;
						lostidx1 = enemyidx;
						ret++;
					}	
				}
	
				num = 0;
				n = 0;
				pow = 1;
				enemyidx = -1;
	
				for (i = 0; i < ROWS; i++)
				{
					if (matrix[i * COLS + xidx])
					{
						n += pow;
	
						if (matrix[i * COLS + xidx] != role)
						{
							// at edge, not in middle
							if ((num == 0) || (num == 2))
							{
								enemyidx = i * COLS + xidx;
							}
						}
						else
						{
							num++;
						}
					}
	
					pow *= 10;
				}
	
				if ((n == 111) || (n == 1110) || (n == 11100))
				{
					if ((num == 2) && (enemyidx >= 0))
					{
						matrix[enemyidx] = 0;
						lostidx2 = enemyidx;
						ret++;
					}	
				}
				
				return ret;
			}

			function roundover(v)
			{
				disable_operation();
				
				//r.text(50, 500, (v == -1) ? "Chanllage failed!" : "You're winner!").attr({"font-size":30, fill: "Green"});
				window.setTimeout(
					function(){
						alert((v == -1) ? "挑战失败!" : "你赢了!");
						enable_operation();
						//window.location .href =window.location .href;
						hismatrix.length = 0;
						init_game(1);
						},
						DELAY_INTERVAL * DELAY_NUM + 1000
					);
			}
			
			function mydelay(e)
			{
				if (e.counter >= DELAY_NUM)
				{
					e.hide();

					if (white_turn)
					{
						window.setTimeout(
							move_white_timer,
							STEP_INTERVAL
							);
						return;
					}

					enable_operation();
					
					return;
				}
				
				if (!(e.counter % 2))
				{
					e.hide();
				}
				else
				{
					e.show();
				}
				
				e.counter++;

				window.setTimeout(mydelay, DELAY_INTERVAL, e);
			}

			function disable_operation()
			{
				var i;
				
				disable_state++; // allow disable anytime
				
				if (disable_state > 2)
				{
					alert("bug, disable_operation");
					disable_state = 2;
				}
				
				if (disable_state > 1)
				{
					return;
				}
				
				for (i = 0; i < myset.length; i++)
				{
					myset[i].undrag();
				}
			}

			function enable_operation()
			{
				var i;

				disable_state--;
				
				if (disable_state < 0)
				{
					alert("bug, enable_operation");
					disable_state = 0;
				}
				
				if (disable_state > 0)
				{
					return;
				}
				
				for (i = 0; i < myset.length; i++)
				{
					myset[i].drag(dragmove, dragstart, dragend);
				}
			}
			
			function judge(set, matrix, e)
			{
				var i;
				var x = e.attr().cx;
				var y = e.attr().cy;
				var xidx = (x - XORG) / XDIS;
				var yidx = (y - YORG) / YDIS;
				var idx = yidx * COLS + xidx;
				var vmatrix = new Array;
				var ret = 0;
	
				for (i = 0; i < TOTALNUM; i++)
				{
					vmatrix[i] = !matrix[i] ? 0 : matrix[i].role;
				}
				
				lostidx1 = -1;
				lostidx2 = -1;
				
				judgeme(vmatrix, idx, e.role);

				if (lostidx1 >= 0)
				{
					disable_operation();
					
					matrix[lostidx1].counter = 0;
					
					window.setTimeout(
						mydelay,
						DELAY_INTERVAL,
						matrix[lostidx1]
						);
					
					//matrix[lostidx1].hide();
					matrix[lostidx1].undrag();
					ret++;
					remove_ele(set, matrix[lostidx1]);
					matrix[lostidx1] = null;
				}
				
				if (lostidx2 >= 0)
				{
					disable_operation();
					
					matrix[lostidx2].counter = 0;
					
					window.setTimeout(
						mydelay,
						DELAY_INTERVAL,
						matrix[lostidx2]
						);
					
					//matrix[lostidx2].hide();
					matrix[lostidx2].undrag();
					ret++;
					remove_ele(set, matrix[lostidx2]);
					matrix[lostidx2] = null;
				}
				
				return ret;
			}
	
			// case 1: enemy is out
			// case 2: enemy cannot move
			function gameover(set)
			{
				var i, w, r;
	
				w = 0;
				r = 0;
	
				for (i = 0; i < set.length; i++)
				{
					if (set[i].role == 1)
					{
						w++;
					}
					else if (set[i].role == 2)
					{
						r++;
					}
					else
					{
						alert("bug, role out of range");
					}
				}
	
				if (w < 2) // red win
				{
					return 1;
				}
	
				if (r < 2) // red fail
				{
					return -1;
				}
	
				return 0;
			}
	
			function push_ele(step, idx, role, num, moveidx, way)
			{
					if (step == 0) // if (moveidx < 0)
					{
						var o = new Object;
						o.idx = idx;
						o.way = way;
						o.role = role;
						o.score = 0;
						best_moves.push(o);
						moveidx = best_moves.length - 1;
					}
	
					if (num > 0)
					{
						if (moveidx < 0)
						{
							alert("bug, moveidx is invalid");
						}
						
						if (role == best_moves[moveidx].role)
						{
							best_moves[moveidx].score += num * Math.pow(10, MAX_STEP - step);
						}
						else
						{
							best_moves[moveidx].score -= num * Math.pow(10, MAX_STEP - step);
						}
					}
			}
			
			// xxx: need to improve
			// 1. dead loop, avoid the same choice...
			// 2. add backoff
			function matrix_deduce(matrix, role, step, moveidx)
			{
				var i, noway = 1;
				var matrixbak;
				var o;
				var ret = 0;
	
				if (step >= MAX_STEP)
				{
					return 0;
				}
				
				for (i = 0; i < TOTALNUM; i++)
				{
					if (matrix[i] == role)
					{
						// can left
						if (((i % COLS) != 0) && !(matrix[i - 1]))
						{
	 						matrixbak = matrix.slice(0);
							matrixbak[i - 1] = matrix[i];
							matrixbak[i] = 0;
							ret = judgeme(matrixbak, i - 1, role);
	
							noway = 0;
							push_ele(step, i, role, ret, moveidx, LEFT);
	
							matrix_deduce(matrixbak, (role == 1) ? 2 : 1, step + 1, (moveidx < 0) ? best_moves.length - 1 : moveidx);
						}
	
						// can right
						if (((i % COLS) != (COLS - 1)) && !(matrix[i + 1]))
						{
							matrixbak = matrix.slice(0);
							matrixbak[i + 1] = matrix[i];
							matrixbak[i] = 0;
							ret = judgeme(matrixbak, i + 1, role);
	
							noway = 0;
							push_ele(step, i, role, ret, moveidx, RIGHT);
	
							matrix_deduce(matrixbak, (role == 1) ? 2 : 1, step + 1, (moveidx < 0) ? best_moves.length - 1 : moveidx);
						}
	
						// can up
						if ((i > (COLS - 1)) && !(matrix[i - COLS]))
						{
							matrixbak = matrix.slice(0);
							matrixbak[i - COLS] = matrix[i];
							matrixbak[i] = 0;
							ret = judgeme(matrixbak, i - COLS, role);
	
							noway = 0;
							push_ele(step, i, role, ret, moveidx, UP);
	
							matrix_deduce(matrixbak, (role == 1) ? 2 : 1, step + 1, (moveidx < 0) ? best_moves.length - 1 : moveidx);
						}
	
						// can down
						if ((i < (TOTALNUM - COLS)) && !(matrix[i + COLS]))
						{
							matrixbak = matrix.slice(0);
							matrixbak[i + COLS] = matrixbak[i];
							matrixbak[i] = 0;
							ret = judgeme(matrixbak, i + COLS, role);
	
							noway = 0;
							push_ele(step, i, role, ret, moveidx, DOWN);
	
							matrix_deduce(matrixbak, (role == 1) ? 2 : 1, step + 1, (moveidx < 0) ? best_moves.length - 1 : moveidx);
						}
					}
				}
	
				if ((step != 0) && noway)
				{
					if (role == best_moves[moveidx].role)
					{
							best_moves[moveidx].score -= Math.pow(10, MAX_STEP - step);
					}
					else
					{
							best_moves[moveidx].score += Math.pow(10, MAX_STEP - step);
					}
					
					return 0;
				}
				
				return noway;
			}
	
			function sortNumber(a, b)
			{
				return b.score - a.score;
			}
			
			function caculate_best_move(matrix, answer)
			{
				// clear best_moves
				best_moves.length = 0;
				
				// noway
				if (matrix_deduce(matrix, 1, 0, -1))
				{
					return 0;
				}

				var which;
				
				if (METHOD)
				{
					var i, which, score;
				
					score = Math.pow(10, MAX_STEP + 1);
					score = -score;
		
					for (i = 0; i < best_moves.length; i++)
					{
						//alert(best_moves[i].score + " " + best_moves[i].idx + " " + best_moves[i].way);
						if (best_moves[i].score > score)
						{
							score = best_moves[i].score;
							which = i;
						}
					}
				}
				else
				{
					best_moves.sort(sortNumber);
					which = Math.round(Math.random());
	
					if (which > best_moves.length - 1)
					{
						which = best_moves.length - 1;
					}
				}
				
				//alert("haha length " + best_moves.length + " score " + best_moves[which].score + " idx " + best_moves[which].idx + " way " + best_moves[which].way);
				answer.which = best_moves[which].idx;
				answer.whichway = best_moves[which].way;
				
				return 1;
			}

			// we're in disabled state when move_white is being called
			function move_white()
			{
				var i, vmatrix = new Array;
				var answer = new Object;
				
				answer.which = 0;
				answer.whichway = 0;

				for (i = 0; i < TOTALNUM; i++)
				{
					vmatrix[i] = !mymatrix[i] ? 0 : mymatrix[i].role;
				}

				if (caculate_best_move(vmatrix, answer))
				{
					// 0: left, 1: right, 2: up, 3: down
					var dxs = [ -XDIS, XDIS, 0, 0 ];
					var dys = [ 0, 0, -YDIS, YDIS ];
					var newwhich;
					var which = answer.which, whichway = answer.whichway;

					if (!mymatrix[which] || (mymatrix[which].role != 1))
					{
						alert("bug, not white role");
					}

					mymatrix[which].go(whichway);

					newwhich = myidx(mymatrix[which].attr().cx, mymatrix[which].attr().cy);
					mymatrix[newwhich] = mymatrix[which];
					mymatrix[which] = null;

					if (judge(myset, mymatrix, mymatrix[newwhich]))
					{
						var ret = gameover(myset);

						if (ret)
						{
							enable_operation();
							roundover(ret);
							return;
						}
					}

					enable_operation();
					
					return;
				}

				// xxx ???
				enable_operation();
				roundover(1);
				return;
			}

			function move_white_timer()
			{
					white_turn = 0;
					move_white();
			}
			
			function dragend(){
				if (this.failed)
				{
					this.failed = 0;
					return;
				}
	
				this.attr({fill: this.oldfill, stroke: "#000"});
	
				var tmpx = this.attr().cx;
				var tmpy = this.attr().cy;
	
				if ((Math.abs(tmpx - this.oldcx) > XDIS + DISDELTA) || (Math.abs(tmpy - this.oldcy) > YDIS + DISDELTA))
				{
					this.attr({cx:this.oldcx, cy:this.oldcy});
					return;
				}
	
				(Math.abs(tmpx - (this.oldcx - XDIS)) < DISDELTA) && (tmpx = this.oldcx - XDIS);
				(Math.abs(tmpx - (this.oldcx + XDIS)) < DISDELTA) && (tmpx = this.oldcx + XDIS);
				
				(Math.abs(tmpy - (this.oldcy - YDIS)) < DISDELTA) && (tmpy = this.oldcy - YDIS);
				(Math.abs(tmpy - (this.oldcy + YDIS)) < DISDELTA) && (tmpy = this.oldcy + YDIS);
	
				(tmpx < XORG) && (tmpx = XORG);
				(tmpx > XORG + XDIS * (COLS - 1)) && (tmpx = XORG + XDIS * (COLS - 1));
	
				(tmpy < YORG) && (tmpy = YORG);
				(tmpy > YORG + YDIS * (ROWS - 1)) && (tmpy = YORG + YDIS * (ROWS - 1));
	
				// xxx: no need for round
				tmpx = Math.round((tmpx - XORG) / XDIS) * XDIS + XORG;
				tmpy = Math.round((tmpy - YORG) / YDIS) * YDIS + YORG;
	
				if (((tmpx == this.oldcx) && (tmpy == this.oldcy)) || ((tmpx != this.oldcx) && (tmpy != this.oldcy)))
				{
					this.attr({cx:this.oldcx, cy:this.oldcy});
					return;
				}
	
				var i;
	
				for (i = 0; i < myset.length; i++)
				{
					if ((myset[i] != this) && (myset[i].attr().cx == tmpx) && (myset[i].attr().cy == tmpy))
					{
						break;
					}
				}
	
				if (i < myset.length)
				{
					this.attr({cx:this.oldcx, cy:this.oldcy});
					return;
				}
				else
				{
					// save history matrix
					{
						var i;
						var vmatrix = new Array;
						
						for (i = 0; i < TOTALNUM; i++)
						{
							vmatrix[i] = !mymatrix[i] ? 0 : mymatrix[i].role;
						}
						
						hismatrix.push(vmatrix);
					}

					var oldidx = myidx(this.oldcx, this.oldcy);
	
					this.attr({cx:tmpx, cy:tmpy});
	
					var newidx = myidx(this.attr().cx, this.attr().cy);

					if (mymatrix[newidx])
					{
						alert("bug, dragend");
					}
	
					mymatrix[oldidx] = null;
					mymatrix[newidx] = this;
	
					if (judge(myset, mymatrix, this))
					{
						var ret = gameover(myset);
	
						if (ret)
						{
							roundover(ret);
							return;
						}

						white_turn = 1;
					}
					else
					{
						white_turn = 1;
						disable_operation();

						window.setTimeout(
							move_white_timer,
							STEP_INTERVAL
							);
					}
				}
			}
			
			function init_game(initcur)
			{
				var i;
				
				if (initcur)
				{
					initial_curmatrix();
				}
				
				initial_mymatrix(myset, mymatrix);

				for (i = 0; i < myset.length; i++)
				{
					myset[i].drag(dragmove, dragstart, dragend);
				}

				//goback, restart...
				if (disable_state || white_turn)
				{
					alert("bug, init_game");
				}
			}

			window.onload = function ()
			{
				r = Raphael("holder", 450, 550);
		    //r.text(50, 500, "Result:").attr({"font-size":30, fill: "Green"});
		    
				var i, j;
		
				for (i = 0; i < ROWS - 1; i++)
				{
					for (j = 0; j < COLS - 1; j++)
					{
						r.rect(XDIS * j + XORG, YDIS * i + YORG, XDIS, YDIS).attr({fill: "none", stroke: "#666"});
					}
				}

				init_game(1);
				
				$("#level").val(MAX_STEP);
				
				// deal with IE setTimeout/setInterval bug
				(function(w){
					//if(!+[1,])
					{
						(function(overrideFun){
							window.setTimeout = overrideFun(window.setTimeout);
							window.setInterval = overrideFun(window.setInterval);
						})(
							function(originalFun){
								return function(code, delay){
									var args = [].slice.call(arguments, 2);
									return originalFun(
										function(){
											if (typeof code == 'string') {
												eval(code);
											}
											else {
												code.apply(this, args);
											}
										},
										delay
									)
								}
							}
						);
					}
				})(window)

			};

			function click_method()
			{
				var optval = $("#method").val();

				if ((optval != "礼貌") && (optval != "贪婪"))
				{
					alert("模式不存在！");
					return;
				}

				METHOD = (optval == "Polite") ? 0 : 1;

				// clear history
				hismatrix.length = 0;
				
				init_game(1);				
			}
			
			function click_level()
			{
				var optval;

				if (disable_state)
				{
					return;
				}
				
				optval = parseInt($("#level").val());

				if ((optval < 1) || (optval > 10))
				{
					alert("级别不存在 " + optval);
					return;
				}

				MAX_STEP = optval;

				// clear history
				hismatrix.length = 0;
				
				init_game(1);				
			}
			
			function click_restart()
			{
				if (disable_state)
				{
					return;
				}
				
				// clear history
				hismatrix.length = 0;
				
				init_game(1);
				//window.location.href = window.location.href;
			}
			
			function click_goback()
			{
				if (disable_state)
				{
					return;	
				}
				
				if (hismatrix.length < 1)
				{
					alert("无路可退!");
					return;
				}

				curmatrix = hismatrix.pop();

				init_game(0);
			}
		
    	</script>
    </head>
    <body>
			<div id="holder"></div>
			<div id="select1">
			<text  style="color:black;">模式:</text>
			<select id="method" onchange="click_method()">
				<option>礼貌</option>
				<option>贪婪</option>
			</select>
			</div>
			<div id="select2">
			<text  style="color:black;">级别:</text>
			<select id="level" onchange="click_level()">
				<option>1</option>
				<option>2</option>
				<option>3</option>
				<option>4</option>
				<option>5</option>
				<option>6</option>
				<option>7</option>
				<option>8</option>
			</select>
			</div>
			<button id="restart" onclick="click_restart()" style="border-width:1px">重来</button>
			<button id="goback" onclick="click_goback()" style="border-width:1px">回退</button>
			<div id="rule">
			<a href="http://baike.baidu.com/link?url=WAjy_pB1B1pD5eE1Q_AQwVtvinLioVI2fZ2ze6QbQyjNLLoCBuWn0tukEUrge59M">规则</a>
			</div>
    </body>
</html>